﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CardGame;
using Cards;
using PokerStatisticsCalculator;
using TexasHoldem.Hands;

namespace TexasHoldem
{
    public class Game : GameBase
    {
        private Table table;

        public Game(params Player[] players)
        {
            this.table = new Table(1000m, players);
        }

        public void PlayRound()
        {
            using (new Round(this.table))
            {
                foreach (var player in this.table.Players)
                {
                    var hand = this.table.PocketCards[player];
                    Console.WriteLine("Player \"{0}\" got cards: {1}, {2}", player, hand.Item1, hand.Item2);
                }

                var communityCards = this.table.CommunityCards;

                Console.WriteLine("Community Cards are: {0}, {1}, {2}, {3}, {4}", communityCards.Item1, communityCards.Item2, communityCards.Item3, communityCards.Item4, communityCards.Item5);

                var bestHands = this.table.Players.Select(player => new Tuple<Player, HandBase>(player, GetBestHand(this.table.PocketCards[player], communityCards))).ToList();

                Console.WriteLine();
                var orderedHands = bestHands.OrderByDescending(tuple => tuple.Item2).ToArray();

                foreach (var player in orderedHands)
                {
                    Console.WriteLine("Player \"{0}\" has best hand {1}: {2}", player.Item1, player.Item2.HandType, player.Item2.OrderedCards.GetString());
                }

                Console.WriteLine();
                var winners = orderedHands.TakeWhile(tuple => tuple.Item2.CompareTo(orderedHands[0].Item2) == 0);
                foreach (var player in winners)
                {
                    Console.WriteLine("Player \"{0}\" is the winner!", player);
                }
            }
        }

        private HandBase GetBestHand(Tuple<Card, Card> pocketCards, Tuple<Card, Card, Card, Card, Card> communityCards)
        {
            HandBase strongestHand = null;
            foreach (var subset in PermutationsCalculator.GetAllPossiblePermutationsWithoutOrder(new Card[] { communityCards.Item1, communityCards.Item2, communityCards.Item3, communityCards.Item4, communityCards.Item5 }, 3))
            {
                var cards = new Tuple<Card, Card, Card, Card, Card>(pocketCards.Item1, pocketCards.Item2, subset[0], subset[1], subset[2]);
                var currentHand = GetHand(cards);
                if (strongestHand == null)
                {
                    strongestHand = currentHand;
                }
                else
                {
                    if (currentHand.CompareTo(strongestHand) > 0)
                    {
                        strongestHand = currentHand;
                    }
                }
            }
            return strongestHand;
        }

        private HandBase GetHand(Tuple<Card, Card, Card, Card, Card> cards)
        {
            Card[] cardArray = new Card[] { cards.Item1, cards.Item2, cards.Item3, cards.Item4, cards.Item5 };

            var orderedCards = cardArray.OrderByDescending(card => (int)card.Rank).ToArray();
            if (cardArray.All(card => card.Suit == cards.Item1.Suit))
            {
                if (Enumerable.Range(1, 4).All(index => (int)cardArray[index].Rank - (int)cardArray[index - 1].Rank == 1))
                    return new StraightFlush(orderedCards);
                else
                    return new Flush(orderedCards);
            }

            var distictsRanks = orderedCards.Select(card => card.Rank).Distinct().ToArray();

            if (distictsRanks.Length == 2)
            {
                int cardsInRank = cardArray.Where(card => card.Rank == distictsRanks[0]).Count();
                if (cardsInRank == 1 || cardsInRank == 4)
                {
                    return new FourOfAKind(orderedCards);
                }
                else
                {
                    return new FullHouse(orderedCards);
                }
            }

            if (Enumerable.Range(1, 4).All(index => (int)cardArray[index].Rank - (int)cardArray[index - 1].Rank == 1))
            {
                return new Straight(orderedCards);
            }

            if (distictsRanks.Length == 3)
            {
                var maxLengthOfRank = distictsRanks.Select(rank => cardArray.Where(card => card.Rank == rank).Count()).Max();
                if (maxLengthOfRank == 3)
                    return new ThreeOfAKind(orderedCards);
                else
                    return new TwoPair(orderedCards);
            }

            if (distictsRanks.Length == 4)
            {
                return new OnePair(orderedCards);
            }

            return new HighCard(orderedCards);
        }
    }
}